//
//  OpenFile.cpp
//  UnixV7FSChecker
//
//  Created by Bram Buurlage on 7/11/12.
//  Copyright (c) 2012 Bram Buurlage. All rights reserved.
//

#include <iostream>
#include <cstring>
#include "FileSystem.h"

int searchForINodeInDirectoryBlock(block* b, const char* inodename)
{
    int offset = 0;
    
    for (int i=0; i<32; ++i)
    {
        short inodenr;
        char name[15];
        name[14] = '\0';
        
        b->readInto(inodenr, offset);
        b->readIntoArray(name, 14, offset);
        
        if (inodenr > 0)
        {
            if (strcmp(name, inodename) == 0)
            {
                std::cout << "found " << name << std::endl;
                return inodenr;
            }
        }
    }
    
    return 0;
}

int searchForINodeInIndirectiveBlock(FileSystem* fs, block* b, const char* inodename)
{
    long addresses[128];
    int offset = 0;
    
    b->readIntoArray(addresses, 128, offset);
    
    int inodenr = 0;
    for (int i=0; i<128; ++i)
    {
        if (addresses[i] == 0) break;
        
        block* blockWithFiles = fs->readBlock(addresses[i]);
        inodenr = searchForINodeInDirectoryBlock(blockWithFiles, inodename);
        
        if (inodenr) return inodenr;
    }
    
    return inodenr;
}

int searchForINodeInDoubleIndirectiveBlock(FileSystem* fs, block* b, const char* inodename)
{
    long addresses[128];
    int offset = 0;
    
    b->readIntoArray(addresses, 128, offset);
    
    int inodenr = 0;
    for (int i=0; i<128; ++i)
    {
        if (addresses[i] == 0) break;
        
        block* blockWithBlocks = fs->readBlock(addresses[i]);
        inodenr = searchForINodeInIndirectiveBlock(fs, blockWithBlocks, inodename);
        
        if (inodenr) return inodenr;
    }
    
    return inodenr;
}

int searchForINodeInTripleIndirectiveBlock(FileSystem* fs, block* b, const char* inodename)
{
    long addresses[128];
    int offset = 0;
    
    b->readIntoArray(addresses, 128, offset);
    
    int inodenr = 0;
    for (int i=0; i<128; ++i)
    {
        if (addresses[i] == 0) break;
        
        block* blockWithDoubleIndirectives = fs->readBlock(addresses[i]);
        inodenr = searchForINodeInDoubleIndirectiveBlock(fs, blockWithDoubleIndirectives, inodename);
        
        if (inodenr) return inodenr;
    }
    
    return inodenr;
}

OpenFile* FileSystem::open(const char *path)
{
    char* pt = new char[strlen(path)+1];
    char* fn;
    
    memcpy(pt, path, strlen(path)+1);
    
    dinode cwd = getInode(itod(ROOTINO));
    
    fn = strtok(pt, "/");
    while (fn != 0x0)
    {
        bool found = false;
        if (cwd.di_mode & X_IFDIR)
        {      
            long addresses[13];
            l3tol(addresses, cwd.di_addr, 13);
            
            for (int a=0; a<13; ++a)
            {
                if (addresses[a] == 0) break;
                
                block* b = readBlock(addresses[a]);
                
                int inodenr = 0;
                switch (a) 
                {
                    case 10:
                        inodenr = searchForINodeInIndirectiveBlock(this, b, fn);
                        break;
                        
                    case 11:
                        inodenr = searchForINodeInDoubleIndirectiveBlock(this, b, fn);
                        break;
                        
                    case 12:
                        inodenr = searchForINodeInTripleIndirectiveBlock(this, b, fn);
                        break;
                        
                    default:
                        inodenr = searchForINodeInDirectoryBlock(b, fn);
                        break;
                }
                
                if (inodenr)
                {
                    found = true;
                    cwd = getInode(inodenr);
                    break;
                }
            }
        }
        
        fn = strtok(0x0, "/");
        if (fn != 0x0 && !found)
        {
            break;
        }
        if (fn == 0x0 && found)
        {
            std::cout << "found file" << std::endl;
            
            //found it! :D
            delete[] pt;
            return 0x0;
        }
    }
    
    delete[] pt;
    return 0x0;
}